主要參考 Test and deploy Laravel applications with GitLab CI/CD and Envoy ,因為這篇文章的順序跟說明都很容易讓人做到懷疑人生,所以本篇文章會稍微修改一下順序及內容,建議閱讀本文章的同時可以邊參考原文。
CI(Continuous integration): 持續整合 => 自動測試CD(Continuous Deployment): 持續部署 => 自動部署[TOC]
Laravel 專案建立後應該都會內建一兩個範例測試,可以透過指令查看這些測試結果。
php artisan test
結果:
  PASS  Tests\Unit\ExampleTest
  ✓ that true is true
  PASS  Tests\Feature\ExampleTest
  ✓ the application returns a successful response
  Tests:  2 passed
  Time:   0.83s
# 命令中的專案位置記得改成你的專案
cd laravel-sample
git init
git remote add origin git@gitlab.example.com:<USERNAME>/laravel-sample.git
git add .
git commit -m 'Initial Commit'
git push -u origin main
官方範例是使用 ACL 這個套件來管理權限,所以我們也裝一下。
sudo apt install acl
裝好後就建立使用者
# Create user deployer
sudo adduser deployer
# Give the read-write-execute permissions to deployer user for directory /var/www
sudo setfacl -R -m u:deployer:rwx /var/www
要先切換至這個使用者,並用 deployer 的身份建立 SSH Key
# 切換使用者
sudo su deployer
切換後就可以建立 Key 了,建立時⚠️記得不要設置密碼⚠️
ssh-keygen -t rsa -b 2048
之後使用指令將公鑰複製到 /authorized_keys
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
並複製 私鑰 並貼到 Gitlab Repo > Settings > CI/CD > Variables
# 顯示私鑰
cat ~/.ssh/id_rsa
同時也要將 公鑰 複製並貼到 Gitlab Repo > Settings > Repository > Deploy Key。頁面上的 Title 隨便填就好
# 顯示公鑰
cat ~/.ssh/id_rsa.pub
以上步驟完成後可以試著 clone 專案看看權限是否設置正常
# 命令中的專案位置記得改成你的專案
git clone git@gitlab.example.com:<USERNAME>/laravel-sample.
如果有顯示 Are you sure you want to continue connecting (yes/no)? 請輸入 yes
這一步要回來我們自己的電腦上面做。照著原文件的做法,會透過 Docker 來做。中間需要在自己機器上 build 過一次,所以本機也會需要安裝 Docker。
原文的版本比較舊,裡面提到的一些設置位置會跟現在 Gitlab 上看到的不同。包含原文說要先開權限,現在版本則預設就已經開好了。
*要部署的伺服器不需要裝 Docker。
原文件是使用 php 7.4 ,但我們專案為 Laravel 9 & php 8.0。有些舊版本套件在 php 8.0 已經無法使用,因此 Dockerfile 的內容會跟原文不同。
在專案根目錄建立 Dockerfile 檔案
# Set the base image for subsequent instructions
FROM php:8.0
# Update packages
RUN apt-get update
# Install PHP and composer dependencies
RUN apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev libzip-dev
# Clear out the local repository of retrieved package files
RUN apt-get clean
# Install needed extensions
# Here you can install any other extension that you need during the test and deployment process
RUN docker-php-ext-install pdo_mysql zip
# Install Composer
RUN curl --silent --show-error "https://getcomposer.org/installer" | php -- --install-dir=/usr/local/bin --filename=composer
# Install Laravel Envoy
RUN composer global require "laravel/envoy" --dev
建立完成後需將 docker 登入至 GitLab registry,輸入完指令會需要輸入你的 Gitlab 帳號密碼。
docker login registry.gitlab.com
之後就能 build、push 映像檔到 Gitlab 上
# 命令中的專案位置記得改成你的專案
docker build -t registry.gitlab.com/<USERNAME>/laravel-sample .
docker push registry.gitlab.com/<USERNAME>/laravel-sample
完成後可以到 Gitlab Repo > Package and registries > Container Registry 找到剛剛 push 上去的 image
在根目錄建立 .gitlab-ci.yml ,這是讓 gitlab 自動運行 CI/CD 的關鍵檔案。設定好後 gitlab 就會將這個檔案的指令丟給上面步驟做的 Docker 去做執行,一切的測試跟部署的調用都會透過 Docker。
與 php 版本一樣,這邊也因應現今版本做了稍微的修改。
image: registry.gitlab.com/<USERNAME>/laravel-sample:latest
services:
  - mysql:latest
variables:
  MYSQL_DATABASE: homestead
  MYSQL_ROOT_PASSWORD: secret
  DB_HOST: mysql
  DB_USERNAME: root
stages:
  - test
  - deploy
unit_test:
  stage: test
  script:
    - cp .env.example .env
    - composer install
    - php artisan key:generate
    - php artisan migrate
    - php artisan test
deploy_production:
  stage: deploy
  script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - ssh-add <(echo "$SSH_PRIVATE_KEY")
    - mkdir -p ~/.ssh
    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
    - ~/.composer/vendor/bin/envoy run deploy --commit="$CI_COMMIT_SHA"
  environment:
    name: production
    url: https://example.com
  when: manual
  only:
    - master
下面幾個小節將會把 .gitlab-ci.yml 中的區塊拆出來解說。
這裡的 SQL 變數是給 Docker 設定預設的資料庫名稱、及帳號密碼,所以這部分我們可以不用動它。
# .gitlab-ci.yml
variables:
  MYSQL_DATABASE: homestead
  MYSQL_ROOT_PASSWORD: secret
  DB_HOST: mysql
  DB_USERNAME: root
因為測試的指令會執行 cp .env.example .env 將範例的環境變數當成正式的環境變數來用。 所以要改的是我們的 .env.example 中的資料庫設定,只要改成跟上面一樣就可以了。
# .env.example
DB_CONNECTION=mysql
DB_HOST=0.0.0.0
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=root
DB_PASSWORD=secret
unit_test:
  script:
    # 安裝 composer 的套件
    - composer install
    # 設置 .env
    - cp .env.example .env
    # 產生 environment key
    - php artisan key:generate
    # 執行 migrations
    - php artisan migrate
    # 執行測試
    - php artisan test
這邊的命令會透過 Envoy 去執行部署,關於 Envoy 的設定我寫在第五章。
因此這邊就看一下底下的 environment、when、only分別的作用。
deploy_production:
  ...
  environment:
    name: production
    url: http://192.168.1.1
  when: manual
  only:
    - main
會顯示在部署完成後 gitlab 上的 Environments 頁面 中的按鈕,分別是按鈕的 名稱(name) 及按鈕的連結(url)。
設定執行部署的時機, manual(手動)。
設定是在哪個 branch 的 commit 推上來後才能夠執行。
調整完後就將這個檔案 commit 並 push 到 gitlab 上。
這個步驟先在自己的機器上做,之後再用 git 推到線上就好,之後會在 docker 內去跑這段程式並部署到你的機器上。
composer require laravel/envoy --dev
並在根目錄建立 Envoy.blade.php:
@servers(['web' => 'deployer@192.168.1.1'])
@setup
    $repository = 'git@gitlab.example.com:<USERNAME>/laravel-sample.git';
    $releases_dir = '/var/www/app/releases';
    $app_dir = '/var/www/app';
    $release = date('YmdHis');
    $new_release_dir = $releases_dir .'/'. $release;
@endsetup
...
記得修改裡面的內容,將 deployer@192.168.1.1 後面的 192.168.1.1 換成你要部署機器的 IP,因為前面有設定過使用者為 deployer 因此就不用改使用者名稱。
*如果要部署多台機器則將其他機器的資料加在後面就行,例如:
@servers([
  'web' => 'deployer@192.168.1.1',
  'web2' => 'deployer@192.168.1.2',
])
部署的檔案位置及資料夾可以自行調整即可。
如果沒修改的話,部署完後 /var/www/app 資料夾會有 releases、storege、current 三個資料夾,並會將 app/storege 及 app/.env 用 link 的方式連接到最新部署的專案資料夾內,並將最新部署的專案 link 到外面的 current 資料夾中。
注意:一開始沒有 storege 以及裡面的資料夾,需要在第一次自動部署前先手動建立,否則網頁打開找不到資料夾就會報錯。
mkdir storege/app
mkdir storage/framework/cache
mkdir storage/framework/sessions
mkdir storage/framework/views
mkdir storage/logs
@servers(['web' => 'deployer@192.168.1.1'])
@setup
    $repository = 'git@gitlab.example.com:<USERNAME>/laravel-sample.git';
    $releases_dir = '/var/www/app/releases';
    $app_dir = '/var/www/app';
    $release = date('YmdHis');
    $new_release_dir = $releases_dir .'/'. $release;
@endsetup
@story('deploy')
    clone_repository
    run_composer
    update_symlinks
@endstory
@task('clone_repository')
    echo 'Cloning repository'
    [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
    git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
    cd {{ $new_release_dir }}
    git reset --hard {{ $commit }}
@endtask
@task('run_composer')
    echo "Starting deployment ({{ $release }})"
    cd {{ $new_release_dir }}
    composer install --prefer-dist --no-scripts -q -o
@endtask
@task('update_symlinks')
    echo "Linking storage directory"
    rm -rf {{ $new_release_dir }}/storage
    ln -nfs {{ $app_dir }}/storage {{ $new_release_dir }}/storage
    echo 'Linking .env file'
    ln -nfs {{ $app_dir }}/.env {{ $new_release_dir }}/.env
    echo 'Linking current release'
    ln -nfs {{ $new_release_dir }} {{ $app_dir }}/current
@endtask
如果有要新增執行的指令也可以自己寫在裡面,像是 npm install 或是 npm build 等等的。
再把 Envoy.blade.php 也 commit 上去後整個流程就完成了。
上面除了第二章建立使用者時會需要登入到你要部署的伺服器 其他都在自己的電腦就能完成。很多配置文件字很多看起來很雜,其實一行一行慢慢看還蠻容易讀懂的。